#ifndef __CFG_H__
#define __CFG_H__

#include <stdint.h>
#include <string.h>
#include "cJSON.h"

#define MAX_VIDEO_CHANNEL  16

#define MACRO2CHAR(macro) (#macro)

#define CJSON_LOAD_STRING(root, node, string) \
    do{ \
        cJSON *__node; \
        if((__node = cJSON_GetObjectItem( root , #node )) != NULL){ \
            if(__node->valuestring){ \
                strncpy(string, __node->valuestring, sizeof(string)); \
                string[sizeof(string) - 1] = '\0';\
            } \
        } \
    }while(0)

#define CJSON_LOAD_STRING_TO_VALUE(root, node, value, list) \
    do{ \
        cJSON *__node; \
        if((__node = cJSON_GetObjectItem( root , #node )) != NULL){ \
            if(__node->valuestring){ \
                value = 0;\
                for(int i = 0; i < sizeof(list) / sizeof(list[0]); i++){ \
                    if(strcmp(list[i],  __node->valuestring) == 0){ \
                        value = i; \
                        break; \
                    } \
                } \
            } \
        } \
    }while(0)

#define CJSON_LOAD_VALUE(root, node, value) \
    do{ \
        cJSON *__node; \
        if((__node = cJSON_GetObjectItem( root , #node )) != NULL){ \
            value = __node->valueint; \
        } \
    }while(0)

#define CJSON_LOAD_FLOAT_VALUE(root, node, value) \
    do{ \
        cJSON *__node; \
        if((__node = cJSON_GetObjectItem( root , #node )) != NULL){ \
            value = __node->valuedouble; \
        } \
    }while(0)

#define CJSON_SET_STRING(root, node, string) \
    do{ \
        cJSON *__node; \
        if((__node = cJSON_GetObjectItem( root , #node )) != NULL){ \
            cJSON_SetString(__node, string); \
        } else { \
            __node = cJSON_CreateString(string); \
            cJSON_AddItemToObject(root, #node, __node); \
        } \
    }while(0)


#define CJSON_SET_VALUE(root, node, value) \
    do{ \
        cJSON *__node; \
        if((__node = cJSON_GetObjectItem( root , #node )) != NULL){ \
            cJSON_SetIntValue(__node, value); \
        } else { \
            __node = cJSON_CreateNumber(value); \
            cJSON_AddItemToObject(root, #node, __node); \
        } \
    }while(0)

#define CJSON_SET_FLOAT_VALUE(root, node, value) \
    do{ \
        cJSON *__node; \
        if((__node = cJSON_GetObjectItem( root , #node )) != NULL){ \
            cJSON_SetIntValue(__node, value); \
        } else { \
            __node = cJSON_CreateNumber(value); \
            cJSON_AddItemToObject(root, #node, __node); \
        } \
    }while(0)

#define CJSON_CREATE_NODE(root, name, node) \
    do{ \
        if((node = cJSON_GetObjectItem(root , name)) == NULL){ \
            node = cJSON_CreateObject(); \
            cJSON_AddItemToObject(root, name, node);\
        } \
    }while(0)

#define CJSON_CREATE_ARRAY(root, name, node) \
    do{ \
        if((node = cJSON_GetObjectItem(root , name)) == NULL){ \
            node = cJSON_CreateArray(); \
            cJSON_AddItemToObject(root, name, node);\
        } \
    }while(0)

#define CJSON_CREATE_ARRAY_ITEM(root, index, item) \
    do{ \
        if((item = cJSON_GetArrayItem(root , index)) == NULL){ \
            item = cJSON_CreateObject(); \
            cJSON_AddItemToArray(root, item);\
        } \
    }while(0)

enum DEVICE_WORK_MODE{
    DEVICE_WORK_MODE_RUNNING = 0,
    DEVICE_WORK_MODE_DEBUG,
    DEVICE_WORK_MODE_UPDATE,
    DEVICE_WORK_MODE_RESERVED,
};

enum VIDEO_MODE{
    VIDEO_4X1080P = 0,
    VIDEO_1X2160P,
};

enum ENCODER_MODE{
    ENCODER_H264 = 0,
    ENCODER_H265,
};

enum INTERLACE_MODE{
    INTERLACE_DISCARD = 0,
    INTERLACE_INTERLEAVED,
};

enum PIXEL_FORMAT{
    TY_420_8BITS = 0,
    TY_422_8BITS,
    TY_420_10BITS,
    TY_422_10BITS,
};

enum PROFILE_MODE{
    TY_AVC_BASELINE = 0,
    TY_AVC_MAIN,
    TY_AVC_HIGH,
    TY_AVC_HIGH10,
    TY_AVC_HIGHI10,
    TY_AVC_HIGH422,
    TY_AVC_HIGHI422,
    TY_HEVC_MAIN,
    TY_HEVC_MAINI,
    TY_HEVC_MAIN10,
    TY_HEVC_MAINI10,
    TY_HEVC_MAIN422,
    TY_HEVC_MAINI422,
};

enum MOSAIC_MODE{
    TY_MOSAIC_4X4 = 0,
    TY_MOSAIC_8X8,
    TY_MOSAIC_16X16,
    TY_MOSAIC_32X32,
    TY_MOSAIC_64X64,
};

typedef struct {
    const char *name;
    int macro;
}macro2char_t;

typedef struct {
    uint32_t width;
    uint32_t height;
    char path[256];

    void* priv;
}debug_config_t;

typedef struct {
    uint32_t mode;
    uint32_t cbpsize;
    uint32_t framerate;
    uint32_t targetbitrate;
    uint32_t maxbitrate;
    uint32_t initialqp;
    uint32_t minqp;
    uint32_t maxqp;
    uint32_t ipdelta;
    uint32_t pbdelta;
}encoder_rc_t;

typedef struct {
    uint32_t mode;
    uint32_t length;
    uint32_t numb;
    uint32_t freqidr;
}encoder_gop_t;

typedef struct {
    uint32_t enable;
    uint32_t encoder;
    uint32_t width;
    uint32_t height;
    uint32_t bitdepth;
    uint32_t uLevel;
    uint32_t profile;
    uint32_t entropymode;
    uint32_t numslices;

    encoder_rc_t rc;
    encoder_gop_t gop;
}encoder_config_t;

typedef struct {
    uint32_t width;
    uint32_t height;
    uint32_t fps;
    uint32_t interlace;
}input_config_t;

typedef struct {
    uint32_t width;
    uint32_t height;
    uint32_t fps;
    uint32_t enable;
}output_config_t;

typedef struct {
    uint32_t width;
    uint32_t height;
    uint32_t pixfmt;
}scaler_param_t;

typedef struct {
    scaler_param_t in;
    scaler_param_t out;
}scaler_config_t;

typedef struct {
    uint32_t posx;
    uint32_t posy;
    uint32_t width;
    uint32_t height;
    uint32_t mode;
}mosaic_config_t;

typedef struct {
    uint32_t channel;
    uint32_t nbuffer;

    input_config_t input_config;
    scaler_config_t scaler_config;
    output_config_t output_config;
    encoder_config_t encode_config;
    mosaic_config_t mosaic_config;

    void *priv;
}video_config_t;

typedef struct {
    uint32_t max_channel;
    uint32_t video_mode;

    video_config_t vcfg[MAX_VIDEO_CHANNEL];
    debug_config_t dcfg[MAX_VIDEO_CHANNEL];
}config_info_t;

typedef struct cfg cfg_t;

typedef struct {
    int32_t (*init)(cfg_t*);
    int32_t (*release)(cfg_t*);

    int32_t (*load_config)(cfg_t*);
    int32_t (*save_config)(cfg_t*);
}cfg_operation_t;

struct cfg{
    const char *input_config_file;
    const char *default_config_file;

    config_info_t *info;

    cfg_operation_t* op;
};

void show_global_config(cfg_t *config);
config_info_t *get_config_info(void);

int32_t config_create(cfg_t **config);
void config_destroy(cfg_t *config);

#endif